import React, { ReactNode, useEffect, useMemo, useRef, useState } from 'react';
import { renderToString } from 'react-dom/server';

import cs from 'classnames';
import type {
  DefaultLabelFormatterCallbackParams,
  EChartsOption,
  EChartsType,
  TooltipComponentFormatterCallback,
  TooltipComponentFormatterCallbackParams
} from 'echarts';
import * as echarts from 'echarts';
import ReactEChartsCore from 'echarts-for-react/lib/core';
import { CallbackDataParams } from 'echarts/types/dist/shared';
import { defaultsDeep, isFunction, isNil } from 'lodash-es';

import { formatNumber } from '@/utils';

import styles from './index.module.scss';
import './theme';
import { getColor } from './util';

export type ChartProps = Omit<React.ComponentProps<typeof ReactEChartsCore>, 'option'> & {
  option: EChartsOption;
  instance?: any;
  /** 单位 */
  extra?: string | string[] | ((p: CallbackDataParams) => ReactNode);
  /** 自动轮播 */
  loop?: boolean | number;
  /** 格式化tooltip的值 */
  formatValue?: (v: any) => string;
};

const Chart: React.FC<ChartProps> = ({
  extra: propsExtra = '',
  loop: propsLoop = 3000,
  formatValue: propsFormatValue,
  option: propsOption,
  onChartReady: propsOnChartReady,
  className,
  ...rest
}) => {
  const instance1 = useRef<EChartsType>();
  const instance = rest?.instance || instance1;
  const [dom, setDom] = useState<HTMLElement>();
  const [colors, setColors] = useState<string[]>([]);
  const unMounted = useRef(false);

  useEffect(() => {
    return () => {
      unMounted.current = true;
    };
  }, []);

  const formatTooltip: TooltipComponentFormatterCallback<
    TooltipComponentFormatterCallbackParams
  > = (params) => {
    const formatValue = (value: any, itemParams: CallbackDataParams) => {
      if (propsFormatValue) {
        return propsFormatValue(value);
      }

      return (
        <>
          {formatNumber(value, 0)}
          {propsExtra ? (
            <span className={styles['tooltip-item-value-unit']}>
              {isFunction(propsExtra) ? propsExtra?.(itemParams) : propsExtra}
            </span>
          ) : null}
        </>
      );
    };

    const renderItem = (
      itemParams: DefaultLabelFormatterCallbackParams,
      nameKey: keyof CallbackDataParams
    ): React.ReactElement => {
      // 过滤值为null/undefined的数据
      if (isNil(itemParams.value)) {
        return <></>;
      }

      const renderValue = Array.isArray(itemParams.value) ? itemParams.value[1] : itemParams.value;
      // console.log(itemParams.seriesName);
      return (
        <div className={styles['tooltip-item']} key={itemParams.dataIndex}>
          <span
            className={styles['tooltip-item-legend']}
            style={{
              background: getColor(itemParams.color)
            }}
          />

          <span className={styles['tooltip-item-label']}>{itemParams[nameKey] as string}</span>
          <span className={styles['tooltip-item-value']}>
            {formatValue(renderValue, itemParams)}
          </span>
        </div>
      );
    };

    if (Array.isArray(params)) {
      if (params.length <= 0) {
        return '';
      }

      const title = (params[0] as Record<string, any>).axisValue;

      return renderToString(
        <>
          {!isNil(title) && <div className={styles['tooltip-title']}>{title}</div>}
          <div className={styles['tooltip-content']}>
            {params.map((v) => renderItem(v, 'seriesName'))}
          </div>
        </>
      );
    }

    const title = (params as Record<string, any>).axisValue;

    return renderToString(
      <>
        {!isNil(title) && <div className={styles['tooltip-title']}>{title}</div>}

        <div className={styles['tooltip-content']}>{renderItem(params, 'name')}</div>
      </>
    );
  };

  useEffect(() => {
    if (!propsLoop) {
      return;
    }

    if (!dom) {
      return;
    }

    if (!Array.isArray(propsOption.series) || !Array.isArray(propsOption.series?.[0].data)) {
      return;
    }

    const len = propsOption.series[0].data.length;

    let index = 0;
    let isHover = false;

    function trigger() {
      if (unMounted.current) {
        return;
      }

      if (!instance.current) {
        return;
      }

      if (isHover) {
        return;
      }

      instance.current.dispatchAction({
        type: 'downplay',
        seriesIndex: 0
      });

      instance.current.dispatchAction({
        type: 'highlight',
        seriesIndex: 0,
        dataIndex: index
      });

      instance.current.dispatchAction({
        type: 'showTip',
        seriesIndex: 0,
        dataIndex: index
      });

      index = (index + 1) % len;
    }

    const interval = propsLoop === true ? 3000 : propsLoop;

    const t = setInterval(trigger, interval);

    function handleHover() {
      isHover = true;
    }

    function handleOut() {
      isHover = false;
    }

    if (dom) {
      dom.addEventListener('mousemove', handleHover);
      dom.addEventListener('mouseout', handleOut);
    }

    // 监听鼠标滑入事件
    instance.current?.on('mouseover', (e: any) => {
      // 鼠标滑入取消整个系列高亮
      instance.current?.dispatchAction({
        type: 'downplay',
        seriesIndex: e.seriesIndex
      });
      // 然后高亮鼠标悬停的那块
      instance.current?.dispatchAction({
        type: 'highlight',
        dataIndex: e.dataIndex
      });
    });

    // 监听鼠标滑出事件
    instance.current?.on('mouseout', (e: any) => {
      // 鼠标滑出取消整个系列所有高亮
      instance.current?.dispatchAction({
        type: 'downplay',
        seriesIndex: e.seriesIndex
      });
      // 然后高亮鼠标最后悬停的那块
      instance.current?.dispatchAction({
        type: 'highlight',
        dataIndex: e.dataIndex
      });
    });

    return () => {
      if (t) {
        clearInterval(t);
      }

      if (dom) {
        dom.removeEventListener('mousemove', handleHover);
        dom.removeEventListener('mouseout', handleOut);
      }
    };
  }, [dom, propsOption.series, propsLoop]);

  const onChartReady: ChartProps['onChartReady'] = (e: EChartsType) => {
    instance.current = e;
    const options = e.getOption() as EChartsOption;
    setColors(options.color as string[]);

    setTimeout(() => {
      if (unMounted.current) {
        return;
      }
      e.resize();
      if (
        Array.isArray(options.series) &&
        Array.isArray(options.series[0].data) &&
        options.series[0].data?.length <= 0
      ) {
        return;
      }
      // 默认高亮第一条数据
      e.dispatchAction({
        type: 'highlight',
        dataIndex: 0
      });

      e.dispatchAction({
        type: 'showTip',
        seriesIndex: 0,
        dataIndex: 0
      });
    });

    setDom(e.getDom());

    propsOnChartReady?.(e);
  };

  const option: ChartOption = useMemo(() => {
    return defaultsDeep({}, propsOption, {
      tooltip: {
        renderMode: 'html',
        formatter: formatTooltip,
        className: styles['tooltip']
      },
      legend: {
        // 不写这个自定义图例有bug
        show: false
      }
    } as ChartOption);
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [propsOption]);

  const selectedLegends: {
    value: number;
    name: string;
  }[] = useMemo(() => {
    const series = Array.isArray(option.series) ? option.series[0] : option.series;

    // @ts-ignore
    if (series && (series.type === 'pie' || series.type === 'pie3d')) {
      return (series.data || []).map((v, index) => {
        if (typeof v === 'number' || typeof v === 'string') {
          return [
            {
              value: v as number,
              name: index.toString()
            }
          ];
        }

        if (Array.isArray(v)) {
          return v.map((item, i) => {
            return {
              value: item as number,
              name: i.toString()
            };
          });
        }

        return {
          value: v.value as number,
          name: v.name as string
        };
      }) as {
        value: number;
        name: string;
      }[];
    }

    return [];
  }, [option]);

  const total = useMemo(() => {
    return selectedLegends.reduce((total, item) => {
      return total + item.value;
    }, 0);
  }, [selectedLegends]);

  return (
    <ReactEChartsCore
      className={cs(styles['chart'], className)}
      option={option}
      onChartReady={onChartReady}
      echarts={echarts}
      lazyUpdate
      notMerge
      theme="dark"
      opts={{
        renderer: 'canvas',
        width: null,
        height: null
      }}
      loadingOption={{
        text: '加载中...',
        textColor: '#fff',
        maskColor: 'rgba(0, 0, 0, 0.1)'
      }}
      {...rest}
      style={{
        position: 'absolute',
        top: 0,
        left: 0,
        height: '100%',
        width: '100%'
      }}
    />
  );
};

export default Chart;
export * from './config';
export type ChartOption = EChartsOption;
